
#ifndef _TTG_EXTRAS_PARAMS_
#define _TTG_EXTRAS_PARAMS_
#include "Common/Definitions.h"

#include <list>

#include "Common/Math.h"
#include "Common/Structures.h"

namespace ttg_internal
{
	//Contains reference to one parameter (which can be constant or not).
	template <class T>
	class ParamLoaderRecord :public ttg::structures::NamedObject
	{
		private:
			T *t;
			const T *t_const;
		public:
			ParamLoaderRecord(const char *name, T *t)
				:NamedObject(name)
					{ this->t = t; this->t_const = t; }
			ParamLoaderRecord(const char *name, const T *t)
				:NamedObject(name)
					{ this->t = NULL; this->t_const = t; }
			T *getParam()
					{ return t; }
			const T *getConstParam() const
					{ return t_const; }
			bool isConst() const
					{ return t == NULL; }
	};
}

namespace ttg
{
	namespace extras
	{
		//Keeps information about one-type parameters.
		template <class T>
		class OneTypeParamLoader
		{
			private:
				const OneTypeParamLoader<T> *parent;
				std::list<T *> locals;
				ttg::structures::Dictionary<ttg_internal::ParamLoaderRecord<T>> *refs;
			public:
				//Creates basic params loader.
				//All records always will be added to son only.
				//But if parent != NULL, then getConstRef() will ask father too.
				inline OneTypeParamLoader(const OneTypeParamLoader<T> *parent = NULL)
				{
					this->refs = Dictionary<ttg_internal::ParamLoaderRecord<T>>::create(20);
					this->parent = parent;
				}
				//Registers referens to T.
				//Note: given pointer must be avaible until ParamLoader exists.
				//Note: All values will be getted directly from this pointer.
				inline void addRef(const char *name, T *ref)
						{ this->refs->add(new ttg_internal::ParamLoaderRecord<T>(name, ref)); }
				inline void addConstRef(const char *name, const T *ref)
						{ this->refs->add(new ttg_internal::ParamLoaderRecord<T>(name, ref)); }
				//Registers value of given T.
				//To change this value, use setValue.
				void addValue(const char *name, const T &val)
				{
					T *t = new T(val);
					locals.push_back(t);
					this->addRef(name, t);
				}
				//Sets new value.
				//Returns true, if such record exists and non-constant. false otherwise.
				bool setValue(const char *name, const T &val)
				{
					ttg_internal::ParamLoaderRecord<T> *record = refs->get(name);
					if (record != NULL && !record->isConst())
					{
						(*record->getParam()) = val;
						return true;
					}
					else
						return false;
				}
				//Gets direct reference to T.
				//Note: this pointer will be avaible until ParamLoader exists.
				//Note: In case of inexistance it returns NULL.
				const T *getConstRef(const char *name) const
				{
					ttg_internal::ParamLoaderRecord<T> *record = refs->get(name);
					if (record != NULL)
					{
						return record->getConstParam();
					}
					else
						if (parent != NULL)
							return parent->getConstRef(name);
						else
							return NULL;
				}
				~OneTypeParamLoader()
				{
					if (refs != NULL)
						delete refs;
					std::list<T *>::iterator it = locals.begin();
					while (it != locals.end())
					{
						if (*it != NULL)
							delete *it;
						it++;
					}
				}
		};
		//Provides info about named params (floats, vectors and matrices),
		//which must be accessable from shader.
		//In the other words: you can name any float/vector/matrix (located anywhere),
		//and use it for calculations or shader bindings.
		class Params
		{
			private:
				OneTypeParamLoader<float> *floats;
				OneTypeParamLoader<math::vec3f> *vectors;
				OneTypeParamLoader<math::mat4x4f> *matrices;
			public:
				//Creates basic params loader. It knows nothing.
				Params(const Params *parent = NULL);
				//Returns vector-params.
				inline OneTypeParamLoader<math::vec3f> &getVectors()
						{ return *vectors; }
				//Returns vector-params.
				inline const OneTypeParamLoader<math::vec3f> &getVectors() const
						{ return *vectors; }
				//Returns matrix-params.
				inline OneTypeParamLoader<math::mat4x4f> &getMatrices()
						{ return *matrices; }
				//Returns matrix-params.
				inline const OneTypeParamLoader<math::mat4x4f> &getMatrices() const
						{ return *matrices; }
				//Returns float-params.
				inline OneTypeParamLoader<float> &getFloats()
						{ return *floats; }
				//Returns float-params.
				inline const OneTypeParamLoader<float> &getFloats() const
						{ return *floats; }
				~Params();
		};
	}
}

#endif
